// $Id: Fig.java 1219 2009-02-04 20:57:24Z bobtarling $
// Copyright (c) 1996-2008 The Regents of the University of California. All
// Rights Reserved. Permission to use, copy, modify, and distribute this
// software and its documentation without fee, and without a written
// agreement is hereby granted, provided that the above copyright notice
// and this paragraph appear in all copies.  This software program and
// documentation are copyrighted by The Regents of the University of
// California. The software program and documentation are supplied "AS
// IS", without any accompanying services from The Regents. The Regents
// does not warrant that the operation of the program will be
// uninterrupted or error-free. The end-user understands that the program
// was developed for research purposes and is advised not to rely
// exclusively on the program for any reason.  IN NO EVENT SHALL THE
// UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
// SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
// ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
// THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
// PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
// CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT,
// UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
package org.tigris.gef.presentation;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Stroke;
import java.awt.event.MouseEvent;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import java.util.Enumeration;
import java.util.List;
import java.util.Vector;

import javax.swing.JMenu;

import org.tigris.gef.base.AlignAction;
import org.tigris.gef.base.CmdReorder;
import org.tigris.gef.base.Editor;
import org.tigris.gef.base.Geometry;
import org.tigris.gef.base.Globals;
import org.tigris.gef.base.Layer;
import org.tigris.gef.base.LayerDiagram;
import org.tigris.gef.base.Selection;
import org.tigris.gef.base.SelectionManager;
import org.tigris.gef.di.GraphicElement;

import org.tigris.gef.graph.GraphEdgeHooks;
import org.tigris.gef.graph.GraphNodeHooks;
import org.tigris.gef.graph.GraphPortHooks;
import org.tigris.gef.graph.MutableGraphSupport;

import org.tigris.gef.properties.PropCategoryManager;

import org.tigris.gef.ui.PopupGenerator;
import org.tigris.gef.undo.Memento;
import org.tigris.gef.undo.UndoManager;

import org.tigris.gef.util.Localizer;

/**
 * This class is the base class for basic drawing objects such as rectangles,
 * lines, text, circles, etc. Also, class FigGroup implements a composite
 * figure. Fig's are Diagram elements that can be placed in any LayerDiagram.
 * Fig's are also used to define the look of FigNodes on NetNodes.
 */
public abstract class Fig implements GraphicElement, Cloneable,
java.io.Serializable, PropertyChangeListener, PopupGenerator {

	/** The smallest size that the user can drag this Fig. */
	public final int MIN_SIZE = 4;

	/** The size of the dashes drawn when the Fig is dashed. */
	private static final String[] DASHED_CHOICES = { "Solid", "Dashed",
		"LongDashed", "Dotted", "DotDash" };
	private static final float[][] DASH_ARRAYS = { null, { 5.0f, 5.0f },
		{ 15.0f, 5.0f }, { 3.0f, 10.0f }, { 3.0f, 6.0f, 10.0f, 6.0f } }; // opaque,
	// transparent,
	// [opaque,
	// transparent]
	private static final int[] DASH_PERIOD = { 0, 10, 20, 13, 25, }; // the
	// sum
	// of
	// each
	// subarray

	/**
	 * Indicates whether this fig can be moved
	 */
	boolean movable = true;

	/**
	 * Indicates whether this fig can be resized
	 */
	boolean resizable = true;

	/**
	 * The Layer that this Fig is in. Each Fig can be in exactly one Layer, but
	 * there can be multiple Editors on a given Layer.
	 */
	private transient Layer layer = null;

	/**
	 * True if this object is locked and cannot be moved by the user.
	 */
	private boolean locked = false;

	/**
	 * Owners are underlying objects that "own" the graphical Fig's that
	 * represent them. For example, a FigNode and FigEdge keep a pointer to the
	 * net-level object that they represent. Also, any Fig can have NetPort as
	 * an owner.
	 * 
	 * @see FigNode#setOwner
	 * @see FigNode#bindPort
	 */
	private transient Object owner;

	/**
	 * X coordinate of the Fig's bounding box. It is the responsibility of
	 * subclasses to make sure this value is ALWAYS up-to-date.
	 */
	protected int _x;

	/**
	 * Y coordinate of the Fig's bounding box. It is the responsibility of
	 * subclasses to make sure this value is ALWAYS up-to-date.
	 */
	protected int _y;

	/**
	 * Width of the Fig's bounding box. It is the responsibility of subclasses
	 * to make sure this value is ALWAYS up-to-date.
	 */
	protected int _w;

	/**
	 * Height of the Fig's bounding box. It is the responsibility of subclasses
	 * to make sure this value is ALWAYS up-to-date.
	 */
	protected int _h;

	/**
	 * Name of the resource being basis to this figs localization.
	 */
	private String resource = "";

	/**
	 * Outline color of fig object.
	 */
	Color _lineColor = Color.black;

	/**
	 * Fill color of fig object.
	 */
	Color _fillColor = Color.white;

	/**
	 * Thickness of line around object, for now limited to 0 or 1.
	 * @deprecated will become private use getLineWidth()
	 */
	int _lineWidth = 1;

	protected float[] _dashes = null;
	protected int _dashStyle = 0;
	protected int _dashPeriod = 0;

	/** True if the object should fill in its area. */
	protected boolean _filled = true;

	/**
	 * The parent Fig of which this Fig is a child
	 */
	private Fig group = null;

	protected String _context = "";

	/**
	 * True if the Fig is visible
	 */
	private boolean visible = true;

	protected boolean _allowsSaving = true;

	private transient boolean _selected = false;

	/**
	 * This flag is set at the start of the removal process. It is later used
	 * for testing to confirm that all removed figs have actually gone from all
	 * layers.
	 */
	private boolean removeStarted;

	// //////////////////////////////////////////////////////////////
	// static initializer
	static {
		// needs-more-work: get rect editor to work
		// PropCategoryManager.categorizeProperty("Geometry", "bounds");
		PropCategoryManager.categorizeProperty("Geometry", "x");
		PropCategoryManager.categorizeProperty("Geometry", "y");
		PropCategoryManager.categorizeProperty("Geometry", "width");
		PropCategoryManager.categorizeProperty("Geometry", "height");
		PropCategoryManager.categorizeProperty("Geometry", "filled");
		PropCategoryManager.categorizeProperty("Geometry", "locked");
		PropCategoryManager.categorizeProperty("Style", "lineWidth");
		PropCategoryManager.categorizeProperty("Style", "fillColor");
		PropCategoryManager.categorizeProperty("Style", "lineColor");
		PropCategoryManager.categorizeProperty("Style", "filled");
	}

	// //////////////////////////////////////////////////////////////
	// geometric manipulations

	/** Margin between this Fig and automatically routed arcs. */
	public final int BORDER = 8;

	/**
	 * Most subclasses will not use this constructor, it is only useful for
	 * subclasses that redefine most of the infrastructure provided by class
	 * Fig.
	 */
	public Fig() {
		an = NoAnnotationStrategy.getInstance();
	}

	/** Construct a new Fig with the given bounds. */
	public Fig(int x, int y, int w, int h) {
		/* Do not set the owner to null when none is given: */
		this(x, y, w, h, Color.black, Color.white);
	}

	/** Construct a new Fig with the given bounds and colors. */
	public Fig(int x, int y, int w, int h, Color lineColor, Color fillColor) {
		/* Do not set the owner to null when none is given: */
		this();
		_x = x;
		_y = y;
		_w = w;
		_h = h;
		if (lineColor != null) {
			_lineColor = lineColor;
		} else {
			_lineWidth = 0;
		}

		if (fillColor != null) {
			_fillColor = fillColor;
		} else {
			_filled = false;
		}

	}

	/** Construct a new Fig with the given bounds, colors, and owner. */
	public Fig(int x, int y, int w, int h, Color lineColor, Color fillColor,
			Object own) {
		this(x, y, w, h, lineColor, fillColor);

		setOwner(own);
		// annotation related
	}

	/**
	 * @deprecated Feature removed. It is unrealistic that different Figs will
	 *             have different locales.
	 */
	public void setResource(String resource) {
		this.resource = resource;
	}

	/**
	 * @deprecated Feature removed. It is unrealistic that different Figs will
	 *             have different locales.
	 */
	public String getResource() {
		return resource;
	}

	// --------------------------------
	// annotation related
	protected AnnotationStrategy an = NoAnnotationStrategy.getInstance();
	protected boolean annotationStatus = false;
	protected Fig annotationOwner;

	// specifies the AnnotationOwner
	public void setAnnotationOwner(Fig f) {
		annotationOwner = f;
		setAnnotationStatus(annotationOwner != null);
	}

	// fig is not an annotation any longer
	public void unsetAnnotationOwner() {
		annotationOwner = null;
		setAnnotationStatus(false);
	}

	public Fig getAnnotationOwner() {
		return annotationOwner;
	}

	/**
	 * USED BY PGML.tee
	 */
	public AnnotationStrategy getAnnotationStrategy() {
		return an;
	}

	/**
	 * Set the AnnotationStrategy for this Fig. using this method will discard
	 * the previous AnnotationStrategy
	 */
	public void setAnnotationStrategy(AnnotationStrategy a) {
		an = a;
	}

	/**
	 * returns true if this fig is an annotation of any other fig
	 */
	public boolean isAnnotation() {
		return annotationStatus;
	}

	// TODO Do we ever want to call this, or just override isAnnotation?
	public void setAnnotationStatus(boolean newValue) {
		annotationStatus = newValue;
	}

	/**
	 * Adds a new Annotation of type "text" to fig.
	 */
	// Only used by PGMLParser
	final public void addAnnotation(Fig annotation, String type, String context) {
	}

	// Unused method
	//
	// final public void removeAnnotation(String context) {
	// }

	final public void removeAnnotation(Fig annotationFig) {
		if (annotationFig.isAnnotation()
				&& (this == annotationFig.getAnnotationOwner())) {
			Globals.curEditor().remove(annotationFig);
			getAnnotationStrategy().removeAnnotation(annotationFig);
		}
	}

	/**
	 * Fig has been moved: Adjust the annotation positions Extracted from
	 * endTrans() so that annotation positions can be updated without redrawing
	 * everything.
	 */
	// TODO This is only required by SelectionManager. Should this be package
	// private
	// an move SelectionManager into this package?
	final public void translateAnnotations() {
		// If this Fig is an annotaion itself, simply store the position at the
		// owner.
		if (this.isAnnotation()) {
			SelectionManager selectionManager = Globals.curEditor()
			.getSelectionManager();
			if (!(selectionManager.containsFig(this.getAnnotationOwner()))
					&& selectionManager.containsFig(this)) {
				(getAnnotationOwner().an).storeAnnotationPosition(this);
			}
		}

		// If this Fig is owner of annotations then move the annotations
		// according to the Fig's own position.
		if (!(getAnnotationStrategy() instanceof NoAnnotationStrategy)) {
			getAnnotationStrategy().translateAnnotations(this);
		}
	}

	/**
	 * Updates the positions of the connected annotations.
	 */
	final public void updateAnnotationPositions() {
		Enumeration annotations = getAnnotationStrategy().getAllAnnotations();
		while (annotations.hasMoreElements()) {
			Fig annotation = (Fig) annotations.nextElement();
			getAnnotationStrategy().storeAnnotationPosition(annotation);
			annotation.endTrans();
		}

		endTrans();
	}

	// Only used by PGMLParser
	final public void initAnnotations() {
	}

	// end annotation related
	// -----------------------------------
	/**
	 * Add a point to this fig. sub classes should implement. TODO: Why isn't
	 * this extended by FigEdgePoly?
	 */
	public void addPoint(int x, int y) {
	}

	/**
	 * The specified PropertyChangeListeners <b>propertyChange</b> method will
	 * be called each time the value of any bound property is changed. Note: the
	 * JavaBeans specification does not require PropertyChangeListeners to run
	 * in any particular order.
	 * <p>
	 * 
	 * Since most Fig's will never have any listeners, and I want Figs to be
	 * fairly light-weight objects, listeners are kept in a global Hashtable,
	 * keyed by Fig. NOTE: It is important that all listeners eventually remove
	 * themselves, otherwise this will prevent garbage collection.
	 */
	final public void addPropertyChangeListener(PropertyChangeListener l) {
		Globals.addPropertyChangeListener(this, l);
	}

	/**
	 * Remove this PropertyChangeListener from the JavaBeans internal list. If
	 * the PropertyChangeListener isn't on the list, silently do nothing.
	 */
	final public void removePropertyChangeListener(PropertyChangeListener l) {
		Globals.removePropertyChangeListener(this, l);
	}

	/**
	 * Align this Fig with the given rectangle. Some subclasses may need to know
	 * the editor that initiated this action.
	 * 
	 * @param r
	 *                the rectangle to align to.
	 * @param direction
	 * @param ed
	 *                the editor that initiated this action.
	 */
	final public void align(Rectangle r, int direction, Editor ed) {
		Rectangle bbox = getBounds();
		int dx = 0;
		int dy = 0;
		switch (direction) {

		case AlignAction.ALIGN_TOPS:
			dy = r.y - bbox.y;
			break;

		case AlignAction.ALIGN_BOTTOMS:
			dy = r.y + r.height - (bbox.y + bbox.height);
			break;

		case AlignAction.ALIGN_LEFTS:
			dx = r.x - bbox.x;
			break;

		case AlignAction.ALIGN_RIGHTS:
			dx = r.x + r.width - (bbox.x + bbox.width);
			break;

		case AlignAction.ALIGN_CENTERS:
			dx = r.x + r.width / 2 - (bbox.x + bbox.width / 2);
			dy = r.y + r.height / 2 - (bbox.y + bbox.height / 2);
			break;

		case AlignAction.ALIGN_H_CENTERS:
			dx = r.x + r.width / 2 - (bbox.x + bbox.width / 2);
			break;

		case AlignAction.ALIGN_V_CENTERS:
			dy = r.y + r.height / 2 - (bbox.y + bbox.height / 2);
			break;

		case AlignAction.ALIGN_TO_GRID:
			Point loc = getLocation();
			Point snapPt = new Point(loc.x, loc.y);
			ed.snap(snapPt);
			dx = snapPt.x - loc.x;
			dy = snapPt.y - loc.y;
			break;
		}

		translate(dx, dy);
	}

	/**
	 * Update the bounds of this Fig. By default it is assumed that the bounds
	 * have already been updated, so this does nothing.
	 * 
	 * @see FigText#calcBounds
	 */
	public void calcBounds() {
	}

	// note: computing non-intersection is faster on average. Maybe I
	// should structure the API to allow clients to take advantage of that?

	/**
	 * Return the center of the given Fig. By default the center is the center
	 * of its bounding box. Subclasses may want to define something else.
	 * 
	 * @deprecated in 0.11.1 Use getCenter();
	 */
	// USED BY PGML.tee
	final public Point center() {
		return getCenter();
	}

	/**
	 * Return the center of the given Fig. By default the center is the center
	 * of its bounding box. Subclasses may want to define something else.
	 */
	// USED BY PGML.tee
	public Point getCenter() {
		Rectangle bbox = getBounds();
		return new Point(bbox.x + bbox.width / 2, bbox.y + bbox.height / 2);
	}

	/**
	 * @deprecated in 0.11.1 use
	 *             org.tigris.gef.persistence.pgml.PgmlUtility.getClassNameAndBounds(Fig)
	 */
	// USED BY PGML.tee
	public String classNameAndBounds() {
		return getClass().getName() + "[" + getX() + ", " + getY() + ", "
		+ getWidth() + ", " + getHeight() + "]";
	}

	public void cleanUp() {
	}

	public Object clone() {
		try {
			return super.clone();
		} catch (CloneNotSupportedException e) {
			return null;
		}
	}

	/**
	 * Can the fig can be copied and pasted
	 */
	public boolean isCopyable() {
		return true;
	}

	/**
	 * Can the fig can be cut and pasted
	 */
	public boolean isCutable() {
		return true;
	}

	/**
	 * Return a point that should be used for arcs that go toward the given
	 * point. By default, this makes arcs end on the edge that is nearest the
	 * given point.
	 * 
	 * needs-more-work: define gravity points, berths
	 */
	public Point connectionPoint(Point anotherPt) {
		List grav = getGravityPoints();
		if (grav != null && grav.size() > 0) {
			int ax = anotherPt.x;
			int ay = anotherPt.y;
			Point bestPoint = (Point) grav.get(0);
			int bestDist = Integer.MAX_VALUE;
			int size = grav.size();
			for (int i = 0; i < size; i++) {
				Point gp = (Point) grav.get(i);
				int dx = gp.x - ax;
				int dy = gp.y - ay;
				int dist = dx * dx + dy * dy;
				if (dist < bestDist) {
					bestDist = dist;
					bestPoint = gp;
				}
			}

			return new Point(bestPoint.x, bestPoint.y);
		}

		return getClosestPoint(anotherPt);
	}

	/**
	 * Reply true if the given point is inside the given Fig. By default reply
	 * true if the point is in my bounding box. Subclasses like FigCircle and
	 * FigEdge do more specific checks.
	 */
	public boolean contains(int x, int y) {
		return (_x <= x) && (x <= _x + _w) && (_y <= y) && (y <= _y + _h);
	}

	/**
	 * Reply true if the given point is inside this Fig by calling contains(int
	 * x, int y).
	 */
	final public boolean contains(Point p) {
		return contains(p.x, p.y);
	}

	/**
	 * Reply true if the all four corners of the given rectangle are inside this
	 * Fig, as determined by contains(int x, int y).
	 */
	final public boolean contains(Rectangle r) {
		return countCornersContained(r.x, r.y, r.width, r.height) == 4;
	}

	/**
	 * Reply the number of corners of the given rectangle that are inside this
	 * Fig, as determined by contains(int x, int y).
	 */
	protected int countCornersContained(int x, int y, int w, int h) {
		int cornersHit = 0;
		if (contains(x, y)) {
			cornersHit++;
		}

		if (contains(x + w, y)) {
			cornersHit++;
		}

		if (contains(x, y + h)) {
			cornersHit++;
		}

		if (contains(x + w, y + h)) {
			cornersHit++;
		}

		return cornersHit;
	}

	/**
	 * Resize the object for drag on creation. It bypasses the things done in
	 * resize so that the position of the object can be kept as the anchor
	 * point. Needs-More-Work: do I really need this function?
	 * 
	 * @see FigLine#createDrag
	 */
	public void createDrag(int anchorX, int anchorY, int x, int y, int snapX,
			int snapY) {
		int newX = Math.min(anchorX, snapX);
		int newY = Math.min(anchorY, snapY);
		int newW = Math.max(anchorX, snapX) - newX;
		int newH = Math.max(anchorY, snapY) - newY;
		setBounds(newX, newY, newW, newH);
	}

	/**
	 * This is called after an Cmd modifies a Fig and the Fig needs to be
	 * redrawn in its new position.
	 */
	public void endTrans() {
		translateAnnotations();
		damage();
	}

	/**
	 * This Fig has changed in some way, tell its Layer to record my bounding
	 * box as a damageAll region so that I will eventualy be redrawn.
	 */
	public void damage() {
		Layer lay = getLayer();
		Fig group = getGroup();
		while (lay == null && group != null) {
			lay = group.getLayer();
			group = group.getGroup();
		}
		if (lay != null) {
			lay.damageAll();
		}
	}

	/**
	 * Get the rectangle on whose corners the dragging handles are to be drawn.
	 * Should be overwritten by Figures with Bounds larger than the HandleBox.
	 * Normally these should be identical.
	 */
	public Rectangle getHandleBox() {
		return getBounds();
	}

	/**
	 * Set the HandleBox. Normally this should not be used. It is intended for
	 * figures where the Handlebox is different from the Bounds. Overide this
	 * method if HandleBox and bounds differ
	 */
	public void setHandleBox(int x, int y, int w, int h) {
		setBounds(x, y, w, h);
	}

	// //////////////////////////////////////////////////////////////
	// Editor API

	/**
	 * Remove this Fig from the Layer it belongs to.
	 */
	public void removeFromDiagram() {

		if (UndoManager.getInstance().isGenerateMementos()) {
			class FigRemoveMemento extends Memento {

				Layer lay;
				Fig fig;
				Fig encFig;
				boolean vis;

				public FigRemoveMemento(Fig f) {
					fig = f;
					lay = fig.getLayer();
					encFig = f.getEnclosingFig();
					vis = fig.isVisible();
				}

				public void undo() {
					UndoManager.getInstance().addMementoLock(this);
					fig.setEnclosingFig(encFig);
					if (lay != null)
						lay.add(fig);
					fig.visible = vis;
					UndoManager.getInstance().removeMementoLock(this);
				}

				public void redo() {
					UndoManager.getInstance().addMementoLock(this);
					fig.removeFromDiagram();
					UndoManager.getInstance().removeMementoLock(this);
				}
			}
			;
			UndoManager.getInstance().addMemento(new FigRemoveMemento(this));
		}

		removeStarted = true;
		visible = false;

		// delete all annotations first
		java.util.Enumeration iter = getAnnotationStrategy()
		.getAllAnnotations();
		while (iter.hasMoreElements()) {
			Fig annotation = (Fig) iter.nextElement();
			getAnnotationStrategy().getAnnotationProperties(annotation)
			.removeLine();
			removeAnnotation(annotation);
			annotation.removeFromDiagram();
		}

		if (layer != null) {
			Layer oldLayer = layer;
			layer.remove(this);
			oldLayer.deleted(this);
		}

		// ak: remove this figure from the enclosed figures of the encloser
		setEnclosingFig(null);
	}

	/**
	 * Delete whatever application object this Fig is representing, the Fig
	 * itself should automatically be deleted as a side-effect. Simple Figs have
	 * no underlying model, so they are just deleted. Figs that graphically
	 * present some part of an underlying model should NOT delete themselves,
	 * instead they should ask the model to dispose, and IF it does then the
	 * figs will be notified.
	 */
	public void deleteFromModel() {
		removeStarted = true;
		Object own = getOwner();
		if (own instanceof GraphNodeHooks) {
			((GraphNodeHooks) own).deleteFromModel();
		} else if (own instanceof GraphEdgeHooks) {
			((GraphEdgeHooks) own).deleteFromModel();
		} else if (own instanceof GraphPortHooks) {
			((GraphPortHooks) own).deleteFromModel();
		} else {
			removeFromDiagram();
		}
	}

	// protected void drawDashedPerimeter(Graphics g) {
	// Point segStart = new Point();
	// Point segEnd = new Point();
	// int numDashes = _dashes.length;
	// int length = getPerimeterLength();
	// int i = 0;
	// int d = 0;
	// while(i < length) {
	// stuffPointAlongPerimeter(i, segStart);
	// i += _dashes[d];
	// d = (d + 1) % numDashes;
	// stuffPointAlongPerimeter(i, segEnd);
	// g.drawLine(segStart.x, segStart.y, segEnd.x, segEnd.y);
	// i += _dashes[d];
	// d = (d + 1) % numDashes;
	// }
	// }
	//

	public void drawRect(Object graphicsContext, boolean filled,
			Color fillColor, int lineWidth, Color lineColor, int x, int y,
			int w, int h, boolean dashed, float dashes[], int dashPeriod) {
		Graphics g = (Graphics) graphicsContext;
		if (filled && fillColor != null) {
			int xx = x;
			int yy = y;
			int ww = w;
			int hh = h;
			if (lineColor != null) {
				if (lineWidth > 1 && !dashed) {
					int lineWidth2 = lineWidth * 2;
					g.setColor(lineColor);
					g.fillRect(xx, yy, ww, hh);
					xx += lineWidth;
					yy += lineWidth;
					ww -= lineWidth2;
					hh -= lineWidth2;
				}
			}
			g.setColor(fillColor);
			g.fillRect(xx, yy, ww, hh);
			if (lineColor != null) {
				if (lineWidth == 1 || dashed) {
					paintRectLine(g, xx, yy, ww, hh, lineWidth, lineColor,
							dashed, dashes, dashPeriod);
				}
			}
		} else {
			paintRectLine(g, x, y, w, h, lineWidth, lineColor, dashed, dashes,
					dashPeriod);
		}
	}

	/**
	 * Paint the line of a rectangle without any fill. Manages line width and
	 * dashed lines.
	 * 
	 * @param g
	 *                The Graphics object
	 * @param x
	 *                The x co-ordinate of the rectangle
	 * @param y
	 *                The y co-ordinate of the rectangle
	 * @param w
	 *                The width of the rectangle
	 * @param h
	 *                The height of the rectangle
	 * @param lwidth
	 *                The linewidth of the rectangle
	 */
	private void paintRectLine(Graphics g, int x, int y, int w, int h,
			int lineWidth, Color lineColor, boolean dashed, float dashes[],
			int dashPeriod) {
		if (lineWidth > 0 && lineColor != null) {
			g.setColor(lineColor);
			if (lineWidth == 1) {
				paintRectLine(g, x, y, w, h, dashed, lineWidth, dashes,
						dashPeriod);
			} else {
				int xx = x;
				int yy = y;
				int hh = h;
				int ww = w;

				for (int i = 0; i < lineWidth; ++i) {
					paintRectLine(g, xx++, yy++, ww, hh, dashed, lineWidth,
							dashes, dashPeriod);
					ww -= 2;
					hh -= 2;
				}
			}
		}
	}

	private void paintRectLine(Graphics g, int x, int y, int w, int h,
			boolean dashed, int lineWidth, float dashes[], int dashPeriod) {
		if (!dashed)
			g.drawRect(x, y, w, h);
		else {
			drawDashedRectangle(g, 0, x, y, w, h, lineWidth, dashes, dashPeriod);
		}
	}

	private void drawDashedRectangle(Graphics g, int phase, int x, int y,
			int w, int h, int lineWidth, float dashes[], int dashPeriod) {

		phase = drawDashedLine(g, lineWidth, x, y, x + w, y, phase, dashes,
				dashPeriod);
		phase = drawDashedLine(g, lineWidth, x + w, y, x + w, y + h, phase,
				dashes, dashPeriod);
		phase = drawDashedLine(g, lineWidth, x + w, y + h, x, y + h, phase,
				dashes, dashPeriod);
		phase = drawDashedLine(g, lineWidth, x, y + h, x, y, phase, dashes,
				dashPeriod);
	}

	public int drawDashedLine(Object graphicsContext, int lineWidth, int x1,
			int y1, int x2, int y2, int phase, float[] dashes, int dashPeriod) {
		if (graphicsContext instanceof Graphics2D) {
			return drawDashedLineG2D((Graphics2D) graphicsContext, lineWidth,
					phase, x1, y1, x2, y2, dashes, dashPeriod);
		}
		Graphics g = (Graphics) graphicsContext;
		// Fall back on the old inefficient method of drawing dashed
		// lines. This is required until SVGWriter is converted to
		// extend Graphics2D
		int segStartX;
		int segStartY;
		int segEndX;
		int segEndY;
		int dxdx = (x2 - x1) * (x2 - x1);
		int dydy = (y2 - y1) * (y2 - y1);
		int length = (int) Math.sqrt(dxdx + dydy);
		int numDashes = dashes.length;
		int d;
		int dashesDist = 0;
		for (d = 0; d < numDashes; d++) {
			dashesDist += dashes[d];
			// find first partial dash?
		}

		d = 0;
		int i = 0;
		while (i < length) {
			segStartX = x1 + ((x2 - x1) * i) / length;
			segStartY = y1 + ((y2 - y1) * i) / length;
			i += dashes[d];
			d = (d + 1) % numDashes;
			if (i >= length) {
				segEndX = x2;
				segEndY = y2;
			} else {
				segEndX = x1 + ((x2 - x1) * i) / length;
				segEndY = y1 + ((y2 - y1) * i) / length;
			}

			g.drawLine(segStartX, segStartY, segEndX, segEndY);
			i += dashes[d];
			d = (d + 1) % numDashes;
		}

		// needs-more-work: phase not taken into account
		return (length + phase) % dashesDist;
	}

	private int drawDashedLineG2D(Graphics2D g, int lineWidth, int phase,
			int x1, int y1, int x2, int y2, float[] dashes, int dashPeriod) {
		int dxdx = (x2 - x1) * (x2 - x1);
		int dydy = (y2 - y1) * (y2 - y1);
		int length = (int) (Math.sqrt(dxdx + dydy) + 0.5); // This causes a
		// smaller rounding
		// error of
		// 0.5pixels max. .
		// Seems acceptable.
		Graphics2D g2D = (Graphics2D) g;
		Stroke originalStroke = g2D.getStroke(); // we need this to restore
		// the original stroke
		// afterwards

		BasicStroke dashedStroke = new BasicStroke(lineWidth,
				BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10.0f, dashes,
				(float) phase);
		// (float width, int cap, int join, float miterlimit, float[] dash,
		// float dash_phase)
		g2D.setStroke(dashedStroke);
		g2D.drawLine(x1, y1, x2, y2);
		g2D.setStroke(originalStroke); // undo the manipulation of g

		return (length + phase) % dashPeriod;
	}

	final public void firePropChange(String propName, int oldV, int newV) {
		firePropChange(propName, new Integer(oldV), new Integer(newV));
	}

	/**
	 * Creates a PropertyChangeEvent and calls all registered listeners
	 * propertyChanged() method.
	 */
	final public void firePropChange(String propName, Object oldV, Object newV) {
		Globals.firePropChange(this, propName, oldV, newV);
		if (group != null) {
			PropertyChangeEvent pce = new PropertyChangeEvent(this, propName,
					oldV, newV);
			group.propertyChange(pce);
		}
	}

	final public void firePropChange(String propName, boolean oldV, boolean newV) {
		firePropChange(propName, new Boolean(oldV), new Boolean(newV));
	}

	/**
	 * Return a Rectangle that completely encloses this Fig. Subclasses may
	 * override getBounds(Rectangle).
	 */
	// USED BY PGML.tee
	public final Rectangle getBounds() {
		return getBounds(null);
	}

	/**
	 * Stores the Rectangle that completely encloses this Fig into "return
	 * value" <b>r</b> and return <b>r</b>. If r is <code>null</code> a new
	 * <code>Rectangle</code> is allocated. This version of
	 * <code>getBounds</code> is useful if the caller wants to avoid
	 * allocating a new <code>Rectangle</code> object on the heap.
	 * 
	 * @param r
	 *                the return value, modified to the components bounds
	 * @return r
	 */
	public Rectangle getBounds(Rectangle r) {
		if (r == null) {
			return new Rectangle(_x, _y, _w, _h);
		}
		r.setBounds(_x, _y, _w, _h);
		return r;
	}

	/**
	 * Overrule this if you have a non-rectangular shape, and want the edge to
	 * be able to attach to locations on line-segments.
	 * 
	 * @param anotherPt
	 *                the point (usually outside the fig) to connet to
	 * @return a point on the border of this
	 */
	public Point getClosestPoint(Point anotherPt) {
		return Geometry.ptClosestTo(getBounds(), anotherPt);
	}

	/** Get the dashed attribute * */
	public boolean getDashed() {
		return (_dashes != null);
	}

	/**
	 * @deprecated in 0.11.1 us
	 *             org.tigris.gef.persistence.pgml.PgmlUtility.getDashed(Fig)
	 */
	// USED by PGML.tee
	final public int getDashed01() {
		return getDashed() ? 1 : 0;
	}

	final public String getDashedString() {
		return (_dashes == null) ? DASHED_CHOICES[0] : DASHED_CHOICES[1];
	}

	public Vector getEnclosedFigs() {
		return null;
	}

	/**
	 * USED BY PGML.tee
	 */
	public Fig getEnclosingFig() {
		return null;
	}

	/**
	 * Does this Fig support the concept of "fill color" in principle
	 * 
	 * @return true if the Fig can be filled
	 */
	public boolean hasFillColor() {
		return true;
	}

	public Color getFillColor() {
		return _fillColor;
	}

	/**
	 * @deprecated use isFilled()
	 * @return
	 */
	public boolean getFilled() {
		return _filled;
	}

	public boolean isFilled() {
		return getFilled();
	}

	/**
	 * @deprecated in 0.11.1 us
	 *             org.tigris.gef.persistence.pgml.PgmlUtility.getDashed(Fig)
	 */
	final public int getFilled01() {
		return _filled ? 1 : 0;
	}

	/**
	 * Does this Fig support the concept of "line color" in principle
	 * 
	 * @return true if the Fig can have a line color
	 */
	public boolean hasLineColor() {
		return true;
	}

	/**
	 * USED BY SVG.tee
	 */
	public Color getLineColor() {
		return _lineColor;
	}

	/**
	 * USED BY SVG.tee
	 */
	public int getLineWidth() {
		return _lineWidth;
	}

	/**
	 * TODO: Should be abstract. Is this needed on Fig or FigEdge
	 */
	public Point getFirstPoint() {
		return new Point();
	}

	/**
	 * Overrule this if you want to connect to a limited number of points, and
	 * to points only.
	 * <p>
	 * Instead, if you want to connect to any point on one or more
	 * line-segments, then you should overrule getClosestPoint().
	 * 
	 * @return the list of gravity points.
	 */
	public List getGravityPoints() {
		return null;
	}

	final public Fig getGroup() {
		return group;
	}

	/**
	 * TODO must determine the purpose of this.
	 * 
	 * @return the context of the Fig.
	 */
	// USED BY PGML.tee
	final public String getContext() {
		return _context;
	}

	/**
	 * @deprecated in 0.11.1 Call getHeight() and half it.
	 * @return
	 */
	/*
	 * USED BY PGML.tee
	 */
	final public int getHalfHeight() {
		return _h / 2;
	}

	/**
	 * @deprecated in 0.11.1 Call getHeight() and half it.
	 * @return
	 */
	/*
	 * USED BY PGML.tee
	 */
	final public int getHalfWidth() {
		return _w / 2;
	}

	/*
	 * USED BY PGML.tee
	 */
	public String getId() {
		if (getGroup() != null) {
			String gID = getGroup().getId();
			if (getGroup() instanceof FigGroup) {
				return gID
				+ "."
				+ ((List) ((FigGroup) getGroup()).getFigs())
				.indexOf(this);
			} else if (getGroup() instanceof FigEdge) {
				return gID
				+ "."
				+ (((List) ((FigEdge) getGroup()).getPathItemFigs())
						.indexOf(this) + 1);
			} else {
				return gID + ".0";
			}
		}

		Layer layer = getLayer();
		if (layer == null) {
			return "LAYER_NULL";
		}

		List c = (List) layer.getContents();
		int index = c.indexOf(this);
		return "Fig" + index;
	}

	/**
	 * TODO: Should be abstract. Is this needed on Fig or FigEdge
	 */
	public Point getLastPoint() {
		return new Point();
	}

	final public Layer getLayer() {
		return layer;
	}

	/**
	 * Returns a point that is the upper left corner of the Fig's bounding box.
	 * Implementation creates a new Point instance, consider getX() and getY()
	 * for performance.
	 */
	final public Point getLocation() {
		return new Point(_x, _y);
	}

	final public boolean getLocked() {
		return locked;
	}

	/**
	 * Returns the minimum size of the Fig. This is the smallest size that the
	 * user can make this Fig by dragging. You can ignore this and make Figs
	 * smaller programmitically if you must. TODO: return a single instance of
	 * an immutable Dimension
	 */
	public Dimension getMinimumSize() {
		return new Dimension(MIN_SIZE, MIN_SIZE);
	}

	public int getNumPoints() {
		return 0;
	}

	/**
	 * Return the model element that this Fig represents. USED BY PGML.tee
	 */
	public Object getOwner() {
		return owner;
	}

	/**
	 * Return the length of the path around this Fig. By default, returns the
	 * perimeter of the Fig's bounding box. Subclasses like FigPoly have more
	 * specific logic.
	 */
	public int getPerimeterLength() {
		return _w + _w + _h + _h;
	}

	public Point[] getPoints() {
		return new Point[0];
	}

	public Point getPoint(int i) {
		return null;
	}

	public Vector getPopUpActions(MouseEvent me) {
		Vector popUpActions = new Vector();

		JMenu orderMenu = new JMenu(Localizer.localize("PresentationGef",
		"Ordering"));

		orderMenu.setMnemonic((Localizer.localize("PresentationGef",
		"OrderingMnemonic")).charAt(0));

		orderMenu.add(CmdReorder.BringForward);
		orderMenu.add(CmdReorder.SendBackward);
		orderMenu.add(CmdReorder.BringToFront);
		orderMenu.add(CmdReorder.SendToBack);
		popUpActions.addElement(orderMenu);
		return popUpActions;
	}

	/**
	 * Returns the prefered size of the Fig. This will be useful for automated
	 * layout. By default just uses the current size. Subclasses must override
	 * to return something useful.
	 */
	final public Dimension getPreferredSize() {
		return new Dimension(_w, _h);
	}

	/**
	 * @deprecated in 0.11.1 this should not form part of the API
	 */
	// Used in SVG.TEE
	public String getPrivateData() {
		return "";
	}

	/** Returns the size of the Fig. */
	public Dimension getSize() {
		return new Dimension(_w, _h);
	}

	public String getTipString(MouseEvent me) {
		if (owner == null)
			return toString();
		return owner.toString();
	}

	public Rectangle getTrapRect() {
		return getBounds();
	}

	public boolean getUseTrapRect() {
		return false;
	}

	/**
	 * @deprecated use PgmlUtility.visibilityToString(Fig f)
	 */
	// USED BY PGML.tee
	final public int getVisState() {
		if (isVisible())
			return 1;
		return 0;
	}

	/**
	 * Get the current width of the Fig.
	 */
	// USED BY PGML.tee
	final public int getWidth() {
		return _w;
	}

	/**
	 * Get the current height of the Fig.
	 */
	// USED BY PGML.tee
	final public int getHeight() {
		return _h;
	}

	/**
	 * Get the x position of the Fig.
	 */
	// USED BY PGML.tee
	final public int getX() {
		return _x;
	}

	/**
	 * Get the y position of the Fig.
	 */
	// USED BY PGML.tee
	final public int getY() {
		return _y;
	}

	public int[] getXs() {
		return new int[0];
	}

	public int[] getYs() {
		return new int[0];
	}

	/**
	 * Determine if the given rectangle contains some pixels of the Fig. This is
	 * used to determine if the user is trying to select this Fig. Rather than
	 * ask if the mouse point is in the Fig, I use a small rectangle around the
	 * mouse point so that small objects and lines are easier to select. If the
	 * fig is invisible this method always returns false.
	 * 
	 * @param r
	 *                the rectangular hit area
	 * @return true if the hit rectangle strikes this fig
	 */
	public boolean hit(Rectangle r) {
		if (!isVisible() || !isSelectable())
			return false;
		int cornersHit = countCornersContained(r.x, r.y, r.width, r.height);
		if (_filled) {
			return cornersHit > 0;
		} else {
			return cornersHit > 0 && cornersHit < 4;
		}
	}

	public void insertPoint(int i, int x, int y) {
	}

	/**
	 * Reply true if the object intersects the given rectangle. Used for
	 * selective redrawing and by ModeSelect to select all Figs that are partly
	 * within the selection rectangle.
	 * <p>
	 * Note: comparisons are strict (e.g. '<' instead of '<='), so that figs
	 * with zero height or width are handled correctly.
	 */
	public boolean intersects(Rectangle r) {
		return !((r.x + r.width < _x) || (r.y + r.height < _y)
				|| (r.x > _x + _w) || (r.y > _y + _h));
	}

	/**
	 * Reply true if the object's perimeter intersects the given rectangle. Used
	 * for selective redrawing and by ModeSelect to select all Figs that are
	 * partly within the selection rectangle.
	 * <p>
	 * Note: comparisons are strict (e.g. '<' instead of '<='), so that figs
	 * with zero height or width are handled correctly.
	 */
	public boolean intersectsPerimeter(Rectangle r) {
		return (r.intersectsLine(_x, _y, _x, _y + _h)
				&& r.intersectsLine(_x, _y + _h, _x + _w, _y + _h)
				&& r.intersectsLine(_x + _w, _y + _h, _x + _w, _y) && r
				.intersectsLine(_x + _w, _y, _x, _y));
	}

	/**
	 * Returns true if this Fig can be resized by the user.
	 */
	final public boolean isLowerRightResizable() {
		return false;
	}

	/**
	 * Returns true if this Fig can be moved around by the user.
	 */
	final public boolean isMovable() {
		return movable;
	}

	/**
	 * Returns true if this Fig can be reshaped by the user.
	 */
	public boolean isReshapable() {
		return false;
	}

	/**
	 * Determine if this Fig can be resized
	 * 
	 * @return true if this Fig can be resized by the user.
	 */
	public boolean isResizable() {
		return resizable;
	}

	/**
	 * Determine if this Fig can be selected
	 * 
	 * @return true if this Fig can be selected by the user.
	 */
	public boolean isSelectable() {
		return true;
	}

	/** Returns true if this Fig can be rotated by the user. */
	public boolean isRotatable() {
		return false;
	}

	/**
	 * Returns the current selection state for this item
	 * 
	 * @return True, if the item is currently selected, otherwise false.
	 */
	final public boolean isSelected() {
		return _selected;
	}

	/**
	 * SelectionManager calls this to attempt to create a custom Selection
	 * object when selecting a Fig. Override this only if you have specialist
	 * requirements For a selected Fig. SelectionManger uses its own rules if
	 * this method returns null.
	 * 
	 * @return a specialist Selection class or null to delegate creation to the
	 *         Selection Manager.
	 */
	public Selection makeSelection() {
		return null;
	}

	/**
	 * Method to paint this Fig. By default it paints an "empty" space,
	 * subclasses should override this method.
	 */
	abstract public void paint(Graphics g);

	/**
	 * Method to paint this Fig. By default it paints an "empty" space,
	 * subclasses should override this method.
	 */
	abstract public void appendSvg(StringBuffer sb);

	protected void appendSvgStyle(StringBuffer sb) {
		sb.append(" style='fill:rgb(").append(getFillColor().getRed()).append(
		",").append(getFillColor().getGreen()).append(",").append(
				getFillColor().getBlue()).append(");").append("stroke-width:")
				.append(getLineWidth()).append(";").append("stroke:rgb(")
				.append(getLineColor().getRed()).append(",").append(
						getLineColor().getGreen()).append(",").append(
								getLineColor().getBlue()).append(");'");
	}

	/**
	 * Return a point at the given distance along the path around this Fig. By
	 * default, uses perimeter of the Fig's bounding box. Subclasses like
	 * FigPoly have more specific logic.
	 */
	final public Point pointAlongPerimeter(int dist) {
		Point res = new Point();
		stuffPointAlongPerimeter(dist, res);
		return res;
	}

	public void postLoad() {
	}

	public void postSave() {
	}

	public void preSave() {
	}

	/**
	 * Draw the Fig on a PrintGraphics. This just calls paint.
	 */
	final public void print(Graphics g) {
		paint(g);
	}

	/**
	 * By default just pass it up to enclosing groups. Subclasses of FigNode may
	 * want to override this method.
	 */
	public void propertyChange(PropertyChangeEvent pce) {
		if (group != null) {
			group.propertyChange(pce);
		}
	}

	/**
	 * Force recalculating of bounds and redraw of fig.
	 */
	final public void redraw() {
		Rectangle rect = getBounds();
		setBounds(rect.x, rect.y, rect.width, rect.height);
		damage();
	}

	// TODO: Make sure this is extended in FigEdgePoly and FigPoly
	public void removePoint(int i) {
	}

	/**
	 * Change the back-to-front ordering of a Fig in LayerDiagram. Should the
	 * Fig have any say in it?
	 * 
	 * @see LayerDiagram#reorder
	 * @see CmdReorder
	 */
	final public void reorder(int func, Layer lay) {
		lay.reorder(this, func);
	}

	/**
	 * Reply a rectangle that arcs should not route through. Basically this is
	 * the bounding box plus some margin around all egdes.
	 */
	final public Rectangle routingRect() {
		return new Rectangle(_x - BORDER, _y - BORDER, _w + BORDER * 2, _h
				+ BORDER * 2);
	}

	/**
	 * @deprecated in 0.11.1 This method is not used.
	 */
	final public boolean savingAllowed() {
		return _allowsSaving;
	}

	/**
	 * @deprecated in 0.11.1 This method is not used.
	 */
	final public void setSavingAllowed(boolean newValue) {
		_allowsSaving = newValue;
	}

	/**
	 * Set the bounds of this Fig. Fires PropertyChangeEvent "bounds". This
	 * method can be undone by performing UndoAction.
	 */
	final public void setBounds(final int newX, final int newY,
			final int newWidth, final int newHeight) {

		if (group == null
				&& (newX != _x || newY != _y || newWidth != _w || newHeight != _h)) {
			MutableGraphSupport.enableSaveAction();
			if (UndoManager.getInstance().isGenerateMementos()) {
				Memento memento = new Memento() {
					int oldX = _x;
					int oldY = _y;
					int oldWidth = _w;
					int oldHeight = _h;

					public void undo() {
						setBoundsImpl(oldX, oldY, oldWidth, oldHeight);
						damage();
					}

					public void redo() {
						setBoundsImpl(newX, newY, newWidth, newHeight);
						damage();
					}

					public void dispose() {
					}

					public String toString() {
						return (isStartChain() ? "*" : " ") + "BoundsMemento "
						+ oldX + ", " + oldY + ", " + oldWidth + ", "
						+ oldHeight;
					}
				};
				UndoManager.getInstance().addMemento(memento);
			}
		}
		setBoundsImpl(newX, newY, newWidth, newHeight);
	}

	/** Set the bounds of this Fig. Fires PropertyChangeEvent "bounds". */
	protected void setBoundsImpl(int x, int y, int w, int h) {
		Rectangle oldBounds = getBounds();
		_x = x;
		_y = y;
		_w = w;
		_h = h;
		firePropChange("bounds", oldBounds, getBounds());
	}

	/**
	 * Change my bounding box to the given Rectangle. Just calls setBounds(x, y,
	 * w, h).
	 */
	public final void setBounds(Rectangle r) {
		setBounds(r.x, r.y, r.width, r.height);
	}

	public final void setCenter(Point p) {
		int newX = p.x - (_w / 2);
		int newY = p.y - (_h / 2);
		setLocation(newX, newY);
	}

	/**
	 * USED BY PGML.tee
	 */
	public void setEnclosingFig(Fig f) {
		if (f != null && f != getEnclosingFig() && layer != null) {
			layer.bringInFrontOf(this, f);
			damage();
		}
	}

	/**
	 * Sets the enclosing FigGroup of this Fig. The enclosing group is always
	 * notified of property changes, without need to add a listener.
	 */
	final public void setGroup(Fig f) {
		group = f;
	}

	final public void setContext(String context) {
		_context = context;
	}

	// //////////////////////////////////////////////////////////////
	// accessors

	/**
	 * Sets the Layer that this Fig belongs to. Fires PropertyChangeEvent
	 * "layer".
	 */
	public void setLayer(Layer lay) {
		firePropChange("layer", layer, lay);
		layer = lay;
	}

	/**
	 * Sets the color that will be used if the Fig is filled. If col is null,
	 * turns off filling. Fires PropertyChangeEvent "fillColor", or "filled".
	 */
	public void setFillColor(Color col) {
		if (col == null) {
			if (_fillColor == null)
				return;
		} else {
			if (col.equals(_fillColor))
				return;
		}

		if (col != null) {
			firePropChange("fillColor", _fillColor, col);
			_fillColor = col;
		} else {
			firePropChange("filled", _filled, false);
			_filled = false;
		}

		MutableGraphSupport.enableSaveAction();
	}

	/**
	 * Sets a flag to either fill the Fig with its fillColor or not. Fires
	 * PropertyChangeEvent "filled".
	 */
	public void setFilled(boolean f) {
		firePropChange("filled", _filled, f);
		_filled = f;
	}

	/**
	 * Sets the color to be used if the lineWidth is > 0. If col is null, sets
	 * the lineWidth to 0. Fires PropertyChangeEvent "lineColor", or
	 * "lineWidth".
	 */
	public void setLineColor(Color col) {
		if (col == null) {
			if (_lineColor == null)
				return;
		} else {
			if (col.equals(_lineColor))
				return;
		}
		if (col != null) {
			firePropChange("lineColor", _lineColor, col);
			_lineColor = col;
		} else {
			firePropChange("lineWidth", _lineWidth, 0);
			_lineWidth = 0;
		}
		MutableGraphSupport.enableSaveAction();
	}

	/**
	 * Set the line width. Zero means lines are not drawn. One draws them one
	 * pixel wide. Larger widths are in experimental support stadium
	 * (hendrik@freiheit.com, 2003-02-05). Fires PropertyChangeEvent
	 * "lineWidth".
	 * 
	 * @param w
	 *                The new lineWidth value
	 */
	public void setLineWidth(int w) {
		int newLW = Math.max(0, w);
		firePropChange("lineWidth", _lineWidth, newLW);
		_lineWidth = newLW;
	}

	/** Set line to be dashed or not * */
	public void setDashed(boolean now_dashed) {
		if (now_dashed) {
			_dashes = DASH_ARRAYS[1];
			_dashPeriod = DASH_PERIOD[1];
		} else {
			_dashes = null;
		}
	}

	public void setDashedString(String dashString) {
		setDashed(dashString.equalsIgnoreCase("solid"));
	}

	/**
	 * Move the Fig to the given position. By default translates the Fig so that
	 * the upper left corner of its bounding box is at the location. Fires
	 * property "bounds".
	 */
	final public void setLocation(int x, int y) {
		translate(x - _x, y - _y);
	}

	/** Move the Fig to the given position. */
	public final void setLocation(Point p) {
		setLocation(p.x, p.y);
	}

	/**
	 * Sets whether this Fig is locked or not. Most Cmds check to see if Figs
	 * are locked and will not request modifications to locked Figs. Fires
	 * PropertyChangeEvent "locked".
	 */
	final public void setLocked(boolean b) {
		firePropChange("locked", locked, b);
		locked = b;
	}

	public void setNumPoints(int npoints) {
	}

	/**
	 * Sets the owner object of this Fig. Fires PropertyChangeEvent "owner"
	 */
	public void setOwner(Object own) {
		firePropChange("owner", owner, own);
		owner = own;
	}

	/** Get and set the points along a path for Figs that are path-like. */
	public void setPoints(Point[] ps) {
	}

	public void setPoint(int i, int x, int y) {
	}

	final public void setPoint(int i, Point p) {
		setPoint(i, p.x, p.y);
	}

	public void setPoint(Handle h, int x, int y) {
		setPoint(h.index, x, y);
	}

	final public void setPoint(Handle h, Point p) {
		setPoint(h, p.x, p.y);
	}

	/**
	 * Derived classes should implement this method
	 * 
	 * @deprecated in 0.11.1 this should not form part of the API
	 */
	public void setPrivateData(String data) {
	}

	/** Sets the size of the Fig. Fires property "bounds". */
	final public void setSize(int w, int h) {
		setBounds(_x, _y, w, h);
	}

	/** Sets the size of the Fig. Fires property "bounds". */
	final public void setSize(Dimension d) {
		setSize(d.width, d.height);
	}

	/**
	 * Set the width of the Fig.
	 * <p>
	 * Use this method only if the width property is the only bounds property of
	 * the Fig you wish to amend. If you intend to also change the height use
	 * setSize(int width, int height). If you also intend to amend the location
	 * use setBounds(int x, int y, int width, int height). Calling a single
	 * method will be far more efficient in changing bounds.
	 * 
	 * @param width
	 *                The new width.
	 */
	final public void setWidth(int w) {
		setBounds(_x, _y, w, _h);
	}

	/**
	 * Set the height of the Fig.
	 * <p>
	 * Use this method only if the height property is the only bounds property
	 * of the Fig you wish to amend. If you intend to also change the width use
	 * setSize(int width, int height). If you also intend to amend the location
	 * use setBounds(int x, int y, int width, int height). Calling a single
	 * method will be far more efficient in changing bounds.
	 * 
	 * @param height
	 *                The new height.
	 */
	final public void setHeight(int h) {
		setBounds(_x, _y, _w, h);
	}

	/**
	 * Set the X co-ordinate of the Fig.
	 * <p>
	 * Use this method only if the X property is the only bounds property of the
	 * Fig you wish to amend. If you intend to also change the Y co ordinate use
	 * setLocation(int x, int y). If you also intend to amend the width and/or
	 * height use setBounds(int x, int y, int width, int height). Calling a
	 * single method will be far more efficient in changing bounds.
	 * 
	 * @param x
	 *                The new x co-ordinate
	 */
	final public void setX(int x) {
		setBounds(x, _y, _w, _h);
	}

	public void setXs(int[] xs) {
	}

	/**
	 * Set the Y co-ordinate of the Fig.
	 * <p>
	 * Use this method only if the Y property is the only bounds property of the
	 * Fig you wish to amend. If you intend to also change the X co ordinate use
	 * setLocation(int x, int y). If you also intend to amend the width and/or
	 * height use setBounds(int x, int y, int width, int height). Calling a
	 * single method will be far more efficient in changing bounds.
	 * 
	 * @param y
	 *                The new y co-ordinate
	 */
	final public void setY(int y) {
		setBounds(_x, y, _w, _h);
	}

	public void setYs(int[] ys) {
	}

	/**
	 * Reshape the given rectangle to be my bounding box.
	 * 
	 * @deprecated use getBounds(Rectangle r)
	 */
	final public void stuffBounds(Rectangle r) {
		r.setBounds(_x, _y, _w, _h);
	}

	public void stuffPointAlongPerimeter(int dist, Point res) {
		if (dist < _w && dist >= 0) {
			res.x = _x + (dist);
			res.y = _y;
		} else if (dist < _w + _h) {
			res.x = _x + _w;
			res.y = _y + (dist - _w);
		} else if (dist < _w + _h + _w) {
			res.x = _x + _w - (dist - _w - _h);
			res.y = _y + _h;
		} else if (dist < _w + _h + _w + _h) {
			res.x = _x;
			res.y = _y + (_w + _h + _w + _h - dist);
		} else {
			res.x = _x;
			res.y = _y;
		}
	}

	/**
	 * Change the position of the object from where it is to where it is plus dx
	 * and dy. Often called when an object is dragged. This could be very useful
	 * if local-coordinate systems are used because deltas need less
	 * transforming... maybe. Fires property "bounds". TODO: make final and
	 * subclasses should extend translateImpl The method is undoable by
	 * performing the UndoAction.
	 * 
	 * @param dx
	 *                the x offset
	 * @param dy
	 *                the y offset
	 */
	public void translate(final int dx, final int dy) {
		if (dx == 0 && dy == 0) {
			return;
		}
		if (group == null) {

			class TranslateMemento extends Memento {

				int oldX;
				int oldY;
				int oldWidth;
				int oldHeight;

				TranslateMemento(int currentX, int currentY, int currentWidth,
						int currentHeight) {
					oldX = currentX;
					oldY = currentY;
					oldWidth = currentWidth;
					oldHeight = currentHeight;
				}

				public void undo() {
					setBoundsImpl(oldX, oldY, oldWidth, oldHeight);
					damage();
				}

				public void redo() {
					translateImpl(dx, dy);
					damage();
				}

				public String toString() {
					return (isStartChain() ? "*" : " ") + "TranslateMemento "
					+ oldX + ", " + oldY;
				}
			}
			if (UndoManager.getInstance().isGenerateMementos()) {
				UndoManager.getInstance().addMemento(
						new TranslateMemento(_x, _y, _w, _h));
			}
		}
		MutableGraphSupport.enableSaveAction();
		translateImpl(dx, dy);
	}

	/**
	 * Change the position of the object from where it is to where it is plus dx
	 * and dy. Often called when an object is dragged. This could be very useful
	 * if local-coordinate systems are used because deltas need less
	 * transforming... maybe. Fires property "bounds".
	 */
	protected void translateImpl(int dx, int dy) {
		Rectangle oldBounds = getBounds();
		_x += dx;
		_y += dy;
		firePropChange("bounds", oldBounds, getBounds());
	}

	/**
	 * Reply true if the entire Fig is contained within the given Rectangle.
	 * This can be used by ModeSelect to select Figs that are totally within the
	 * selection rectangle.
	 */
	final public boolean within(Rectangle r) {
		return r.contains(_x, _y) && r.contains(_x + _w, _y + _h);
	}

	/** Returns true if the fig is visible */
	final public boolean isVisible() {
		return visible;
	}

	/**
	 * Set the visible status of the fig
	 */
	public void setVisible(boolean visible) {
		if (this.visible == visible)
			return;
		MutableGraphSupport.enableSaveAction();
		this.visible = visible;
	}

	/**
	 * Set whether this Fig can be resized
	 * 
	 * @param resizable
	 *                true to make this Fig resizable
	 */
	public void setResizable(boolean resizable) {
		this.resizable = resizable;
	}

	/**
	 * Set whether this Fig can be moved
	 * 
	 * @param movable
	 *                true to make this Fig movable
	 */
	public void setMovable(boolean movable) {
		this.movable = movable;
	}

	final public boolean isRemoveStarted() {
		return removeStarted;
	}

} /* end class Fig */
